﻿namespace QuickMail.BusinessLogic
{
    using System;
    using System.IO;
    using System.Management;
    using System.Security.Cryptography;
    using System.Text;

    using QuickMail.DataModels;
    using QuickMail.View.Windows;

    static class CryptWorker
    {
        private const string Salt = "YtLfwW5trvbupEum1IIluA9Bj_ZnsF8nBqQqL8ic";

        /// <summary>
        /// Gets a unique identifier for your computer (serial number of the motherboard), 
        /// and computed hash with salt.
        /// </summary>
        private static string ComputeHash()
        {
            var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_BaseBoard");
            ManagementObjectCollection managementObjects = searcher.Get();
            string hashString = string.Empty;

            foreach (var mo in managementObjects)
            {
                var serial = mo["SerialNumber"];
                if (serial != null)
                {
                    var md5 = MD5.Create();
                    var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(serial + Salt));
                    hashString = BitConverter.ToString(hash).Replace("-", string.Empty);
                    break;
                }
            }

            return hashString;
        }

        /// <summary>
        /// The method of encrypting text.
        /// </summary>
        /// <param name="plainText">Text to encrypt.</param>
        /// <returns>Encrypted text.</returns>
        public static string Encrypt(string plainText)
        {
            if (string.IsNullOrEmpty(plainText))
                return string.Empty;

            byte[] initialVectorBytes = Encoding.ASCII.GetBytes("bKJeRU2GIESbo3hq");
            byte[] saltValueBytes = Encoding.ASCII.GetBytes("dji1saBFqDR8O9zcI8Vm");
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            var derivedPassword = new PasswordDeriveBytes(ComputeHash(), saltValueBytes, "SHA1", 2);
            // ReSharper disable once CSharpWarnings::CS0618
            byte[] keyBytes = derivedPassword.GetBytes(32);
            var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC };

            byte[] cipherTextBytes;

            using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initialVectorBytes))
            {
                using (var memStream = new MemoryStream())
                {
                    using (var cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write))
                    {
                        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                        cryptoStream.FlushFinalBlock();
                        cipherTextBytes = memStream.ToArray();
                        memStream.Close();
                        cryptoStream.Close();
                    }
                }
            }
            
            symmetricKey.Clear();
            return Convert.ToBase64String(cipherTextBytes);
        }

        /// <summary>
        /// The method of decrypting text.
        /// </summary>
        /// <param name="cipherText">Encrypted text.</param>
        /// <returns>Decrypted text.</returns>
        public static string Decrypt(string cipherText)
        {
            try
            {
                if (string.IsNullOrEmpty(cipherText))
                    return string.Empty;

                byte[] initialVectorBytes = Encoding.ASCII.GetBytes("bKJeRU2GIESbo3hq");
                byte[] saltValueBytes = Encoding.ASCII.GetBytes("dji1saBFqDR8O9zcI8Vm");
                byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

                var derivedPassword = new PasswordDeriveBytes(ComputeHash(), saltValueBytes, "SHA1", 2);
                // ReSharper disable once CSharpWarnings::CS0618
                byte[] keyBytes = derivedPassword.GetBytes(32);

                var symmetricKey = new RijndaelManaged { Mode = CipherMode.CBC };

                var plainTextBytes = new byte[cipherTextBytes.Length];
                int byteCount;

                using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initialVectorBytes))
                {
                    using (var memStream = new MemoryStream(cipherTextBytes))
                    {
                        using (var cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Read))
                        {
                            byteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                            memStream.Close();
                            cryptoStream.Close();
                        }
                    }
                }

                symmetricKey.Clear();
                return Encoding.UTF8.GetString(plainTextBytes, 0, byteCount);
            }
            catch (Exception ex)
            {
                StaticFields.Logger.Warn(" -- {0}.{1} -- {2}", typeof(EnvironmentWorker).FullName,
                    System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message);
                return string.Empty;
            }
        }
    }
}
